// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using SnapObjects.Commons;

namespace SnapObjects.Expressions
{
    internal class CompileMethodMatcher : MethodMatcher<Expression, ParameterExpression>
    {
        protected readonly IMetadataManager<Expression> _metadatas;

        public CompileMethodMatcher(IMetadataManager<Expression> metadatas)
        {
            _metadatas = metadatas;
        }

        protected override Expression CreateDefault(Type type, object defaultValue)
        {
            try
            {
                var value = Utils.ChangeType(defaultValue, type);

                return Expression.Constant(value, type);
            }
            catch (Exception)
            {
                var constructors = type.GetConstructors();

                if (constructors.Length > 0)
                {
                    return Expression.New(
                        constructors[0], Expression.Constant(0));
                }
            }

            return Expression.Constant(null);
        }

        protected override Expression CreateParams(Type type, List<Expression> initializers)
        {
            return Expression.NewArrayInit(type, initializers);
        }

        protected override Expression ConvertType(Expression argExpr, Type type)
        {
            return Expression.Convert(argExpr, type);
        }

        protected override Type GetParamExprType(ParameterExpression paramExpr)
        {
            return paramExpr.Type;
        }

        protected override Type GetArgExprType(Expression argExpr)
        {
            return argExpr.Type;
        }

        protected override Expression CharToString(Expression argExpr)
        {
            return DynamicCall.GetExpression("ToString", argExpr);
        }

        protected override bool MatchDefaultParameter(
            ParserOptions<Expression> options, ParameterExpression paramExpr, out Expression outExp)
        {
            if (paramExpr.Type.IsInterface)
            {
                var isOk = _metadatas.TryGetMetadata(
                    MetadataType.Parameter, paramExpr.Type, options, out var parameter);

                if (isOk)
                {
                    outExp = ((ParameterMetadata<Expression>)parameter).Value;
                }
                else
                {
                    outExp = paramExpr;
                }

                if (outExp is ParameterExpression pExpr)
                {
                    ((CompileParserOptions)options).AddUsedParameter(pExpr);
                }

                return true;
            }

            outExp = null;
            return false;
        }
    }
}
